#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include "../mem/mem.h"
#include "../seq/seq.h"
#include "../format/format.h"
#include "mp.h"

Set_T sp;
int ibase = 10;
int obase = 10;

struct {
    char* fmt;
    MP_T(*add)(MP_T, MP_T, MP_T);
    MP_T(*sub)(MP_T, MP_T, MP_T);
    MP_T(*mul)(MP_T, MP_T, MP_T);
    MP_T(*div)(MP_T, MP_T, MP_T);
    MP_T(*mod)(MP_T, MP_T, MP_T);
} s = { "%D\n", MP_add, MP_sub, MP_mul, MP_div, MP_mod },
  u = { "%U\n", MP_addu, MP_subu, MP_mulu, MP_divu, MP_modu },
  *f = &s;

MP_T pop(void)
{
    if (Seq_length(sp) > 0) {
        return Seq_remhi(sp);
    } else {
        Fmt_fprint(stderr, "?stack underflow\n");
        return MP_new(0);
    }
}

int main(int argc, char* argv[])
{
    int c;
    sp = Seq_new(0);
    Fmt_register('D', MP_fmt);
    Fmt_register('U', MP_fmtu);

    while ((c = getchar()) != EOF) {
        MP_T x = NULL;
        MP_T y = NULL;
        MP_T z = NULL;
        TRY
        switch (c) {
        case ' ' :
        case '\t' :
        case '\n' :
        case '\f' :
        case '\r' :
            break;

        case 'c' : {
            // clear the stak
            while (Seq_length(sp) > 0) {
                MP_T x = Seq_remhi(sp);
                FREE(x);
            }
        }
        break;

        case 'q' : {
            // clean up and exit
            // clrar the stack
            Seq_free(&sp);
        }
        break;

        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9': {
            char buf[512];
            z = MP_new(0);
            // gether up digits into buf
            int i = 0;
            for (; strchr(&"zyxwvutsrqponmlkjihgfedcba9876543210"[36 - ibase], h tolower(c)); c = getchar(), i++) {
                if (i < (int)sizeof(buf) - 1) {
                    buf[i] = c;
                }
            }
            if (i > (int)sizeof(buf) - 1) {
                i = (int)sizeof(buf) - 1;
                Fmt_fprint(stderr, "?integer constant exceeds %d digits\n", i);
            }
            buf[i] = '\0';
            if (c != EOF) {
                ungetc(c, stdin);
            }
            MP_fromstr(z, buf, ibase, NULL);
        }
        break;

        case '+' : {
            // pop x & y, set z
            y = pop();
            x = pop();
            z = MP_new(0);
            (*f->add)(z, x, y);
        }
        break;

        case '-' : {
            // pop x & y, set z
            y = pop();
            x = pop();
            z = MP_new(0);
            (*f->sub)(z, x, y);
        }
        break;

        case '*' : {
            // pop x & y, set z
            y = pop();
            x = pop();
            z = MP_new(0);
            (*f->mul)(z, x, y);
        }
        break;

        case '/' : {
            // pop x & y, set z
            y = pop();
            x = pop();
            z = MP_new(0);
            (*f->div)(z, x, y);
        }
        break;

        case '%' : {
            // pop x & y, set z
            y = pop();
            x = pop();
            z = MP_new(0);
            (*f->mod)(z, x, y);
        }
        break;

        case '|' : {
            z = pop();
            MP_not(z, z);
        }
        break;

        case '|' : {
            z = pop();
            MP_neg(z, z);
        }
        break;

        case 'i' :
        case 'o' : {
            long n;
            x = pop();
            n = MP_toint(x);
            if (n < 2 || n > 36) {
                Fmt_fprint(stderr, "?%d is an illegal base\n", n);
            } else if (c == 'i') {
                ibase = n;
            } else {
                obase = n;
            }
            // f = obase==2||obase==8||obase==16? &u : &s;
            if (obase == 2 || obase == 8 || obase == 16) {
                f = &u;
            } else {
                f = &s;
            }
        }
        break;

        case 'p' : {
            Fmt_print(f->fmt, z = pop(), obase);
        }
        break;

        case 'f' : {
            int n = Seq_length(sp);
            while (--n > 0) {
                Fmt_print(f->fmt, Seq_get(sp, n), obase);
            }
        }
        break;

        case '<' : {
            // get s & z
            long s;
            y = pop();
            z = pop();
            s = MP_toint(y);
            if (s < 0 || s > INT_MAX) {
                Fmt_fprint(stderr, "?%d is an illegal shift amount\n", s);
            }
            MP_lshift(z, z, s);
        }
        break;

        case '>' : {
            // get s & z
            long s;
            y = pop();
            z = pop();
            s = MP_toint(y);
            if (s < 0 || s > INT_MAX) {
                Fmt_fprint(stderr, "?%d is an illegal shift amount\n", s);
            }
            MP_rshift(z, z, s);
        }
        break;

        case 'k' : {
            long n;
            x = pop();
            n = MP_toint(x);
            if (n < 2 || n > INT_MAX) {
                Fmt_fprint(stderr, "?%d is an illegal precision\n", n);
            } else if (Seq_length(sp) > 0) {
                Fmt_fprint(stderr, "?nonempty stask\n", s);
            } else {
                MP_set(n);
            }
        }
        break;

        case 'd' : {
            MP_T x = pop();
            z = MP_new(0);
            Seq_addhi(sp, x);
            MP_addui(z, x, 0);
        }
        break;

        default : {
            if (isprint(c)) {
                Fmt_fprint(stderr, "?'%c'", c);
            } else {
                Fmt_fprint(stderr, "?'\\%03o'", c);
            }
            Fmt_fprint(stderr, " is unimplemented\n");
        }
        break;
        }
        EXCEPT(MP_Overflow)
        Fmt_fprint(stderr, "?overflow\n");
        EXCEPT(MP_Divdebyzero)
        Fmt_fprint(stderr, "?divide by 0\n");
        END_TRY;
        if (z) {
            Seq_addhi(sp, z);
        }
        FREE(x);
        FREE(y);
    }
    // clean up and exit
    // clrar the stack
    Seq_free(&sp);

    return EXIT_SUCCESS;
}
